#include"FaceRec.h"
#include"FaceExtract.h"
#include"FaceFile.h"
#include <opencv2/highgui.hpp>
#include<thread>
#include<iostream>
#include<sstream>

#ifdef _DEBUG

#define COMPARE_PATH "test/compare"
#define INPUT_PATH "test/input"
#define OUTPUT_PATH "test/output"
#define ORIGIN_PATH "test/origin"

#ifdef _WIN32 //Windows系统

#include<direct.h>
#define CREATEFILE(x) _mkdir(x)
#define VIDEO_TEST ""
#define IMAGE_TEST ""

#else //mac & linux系统

#include<sys/stat.h>
#define CREATEFILE(x) mkdir(x , S_IRWXO | S_IRWXG | S_IRWXU)
#define VIDEO_TEST "/Users/Bevis/Desktop/Dev/C++ Project/ExtractFaces_cv2/video/5.mp4"
#define IMAGE_TEST ""

#endif

void VideoMode(std::string path);
void FaceRecMode();
bool CreateWindow();
void WriteFile();
void CatchFace();
void Demo();

int main(int argc, char** argv)
{
    CREATEFILE(COMPARE_PATH);
    CREATEFILE(INPUT_PATH);
    CREATEFILE(OUTPUT_PATH);
    CREATEFILE(ORIGIN_PATH);
    std::cout << "😀请选择模式:\n" <<
    "1.摄像头人脸抓取测试\n" <<
    "2.视频人脸抓取测试\n" <<
    "3.本地人脸图片对比\n" <<
    "4.从本地图片中捕获人脸\n" <<
    "5.利用截获的人脸图片写入人脸数据\n" <<
    "6.窗体测试\n" <<
    "7.Demo\n";
    char mode;
    std::cin >> mode;
    
    switch (mode)
    {
        case '1':
            VideoMode("");
            break;
        case '2':
            VideoMode(VIDEO_TEST);
            break;
        case '3':
            FaceRecMode();
            break;
        case '4':
            CatchFace();
            break;
        case '5':
            WriteFile();
            break;
        case '6':
            CreateWindow();
            break;
        case '7':
            Demo();
            break;
        default:
            std::cout << "未知模式!\n";
            break;
    }
    return 0;
}

void VideoMode(std::string path)
{
    FaceExtract fe("model/haarcascade_frontalface_alt2.xml");
    if (fe.HasError())
    {
        std::cout << "error" << std::endl;
        getchar();
        
        exit(0);
    }
    cv::VideoCapture video;
    if (path.empty())
    {
        if (!video.open(0))
        {
            std::cout << "open fail!" << std::endl;
            getchar();
            exit(0);
        }
    }
    else
    {
        if (!video.open(path))
        {
            std::cout << "open fail!" << std::endl;
            getchar();
            exit(0);
        }
    }
    
    video.set(CV_CAP_PROP_FPS, 30);
    cv::UMat frame;
    int i = 0;
    while (1)
    {
        video >> frame;
        cv::resize(frame, frame, cv::Size(), 0.75, 0.75);
        cv::imshow("video", frame);
        i++;
        if (i == 30)
        {
            i = 0;
            fe.AnalyzeFrame(frame);
        }
        while (fe)
        {
            auto face = fe.GiveFace();
            if (!path.empty()) {
                cv::imshow("face", face);
                cv::waitKey(1000);
            }
        }
        if (cv::waitKey(5) >= 0)break;
        //std::this_thread::sleep_for(std::chrono::microseconds(100));
    }
    cv::destroyAllWindows();
}

void FaceRecMode()
{
    std::cout << "默认匹配路径：" << COMPARE_PATH << "\n是否需要更改路径(y/n)：";
    char bol;
    std::cin >> bol;
    cv::String path = COMPARE_PATH;
    if (bol == 'Y' || bol == 'y')
    {
        std::cout << "请输入目标文件夹：\n";
        std::string _path;
        std::cin >> _path;
        path = _path;
    }
    std::vector<cv::String> ss;
    cv::glob(path, ss);
    auto tmp = std::find(ss.begin(), ss.end(), path + "/.DS_Store");
    if (tmp != ss.end())ss.erase(tmp);
    
    if (ss.size() == 0)
    {
        std::cout << "找不到文件，请确认：\"" << path << "\"路径下至少有两张需要对比的图片" << std::endl;
        exit(-2);
    }
    
    std::string modelConfiguration = "model/deploy.prototxt";
    std::string modelBinary = "model/vgg_face.caffemodel";
    FaceRec face(modelConfiguration, modelBinary);
    
    if (!face)
    {
        std::cout << "模型加载错误！" << std::endl;
        exit(-2);
    }
    face.UseGPU(true);
    std::cout << "开始对比！" << std::endl;
    
    for (auto & str1 : ss)
    {
        for (auto & str2 : ss)
        {
            auto img1 = cv::imread(str1);
            auto img2 = cv::imread(str2);
            
            if (img1.empty() || img2.empty())continue;
            
            std::cout << "开始对比：\n" << str1 << '\n' << str2 << std::endl;
            float returnPercision = FaceRec::Similar(face.GetFeatures(img1), face.GetFeatures(img2));
            if (returnPercision > 0.8)std::cout << " 他俩或许是同一个人！";
            else std::cout << "他俩或许不是同一个人！";
            std::cout << "(" << returnPercision * 100 << "%)\n" << std::endl;
        }
    }
}

//创建一个窗体
bool CreateWindow() {
    cvNamedWindow("window", CV_WINDOW_OPENGL | CV_WINDOW_NORMAL);
    
    cvWaitKey();
    return true;
}

void WriteFile()
{
    std::cout << "默认输入路径：" << INPUT_PATH << "\n是否需要更改路径(y/n)：";
    char bol;
    std::cin >> bol;
    cv::String path = INPUT_PATH;
    if (bol == 'Y' || bol == 'y')
    {
        std::cout << "请输入目标文件夹：\n";
        std::string _path;
        std::cin >> _path;
        path = _path;
    }
    std::vector<cv::String> ss;
    cv::glob(path, ss);
    auto tmp = std::find(ss.begin(), ss.end(), path + "/.DS_Store");
    if (tmp != ss.end())ss.erase(tmp);
    if (ss.size() == 0)
    {
        std::cout << "找不到文件，请确认：\"" << path << "\"路径下至少有一张需要记录的人脸图片" << std::endl;
        exit(-2);
    }
    std::string modelConfiguration = "model/deploy.prototxt";
    std::string modelBinary = "model/vgg_face.caffemodel";
    FaceRec face(modelConfiguration, modelBinary);
    if (!face)
    {
        std::cout << "模型加载错误！" << std::endl;
        getchar();
        exit(0);
    }
    face.UseGPU(true);
    std::cout << "开始记录！" << std::endl;
    FaceFile ff;
    for (auto & str : ss)
    {
        std::string path = str;
        cv::Mat mat = cv::imread(str);
        if (mat.empty())continue;
        std::cout << "记录一条\n";
        int end = path.find_last_of('.');
        if (end == -1)end = path.length();
        int start = path.find_last_of('\\') + 1;
        int tmp = path.find_last_of('/') + 1;
        if (start < tmp)start = tmp;
        if (start == -1)start = 0;
        std::stringstream name(path.substr(start, end - start));
        long id;
        name >> id;
        if (!name)id = name.str()[0] << 8;
        std::vector<float> vec = face.GetFeatures(mat);
        ff.Add(std::move(vec), id);
    }
    std::cout << "正在生成！\n";
    if (path[path.length() - 1] != '/' || path[path.length() - 1] != '\\')path += '/';
    ff.Save(path + "facesfeatures.facedata");
    std::cout << "生成成功：" << path + "facesfeatures.facedata" << std::endl;
}

void CatchFace()
{
    std::cout << "默认输出路径：" << OUTPUT_PATH << "请确保此路径存在！\n";
    std::cout << "默认输入路径：" << ORIGIN_PATH << "\n是否需要更改输入路径(y/n)：";
    char bol;
    std::cin >> bol;
    cv::String path = ORIGIN_PATH;
    if (bol == 'Y' || bol == 'y')
    {
        std::cout << "请输入目标文件夹：\n";
        std::string _path;
        std::cin >> _path;
        path = _path;
    }
    std::vector<cv::String> ss;
    cv::glob(path, ss);
    auto tmp = std::find(ss.begin(), ss.end(), path + "/.DS_Store");
    if (tmp != ss.end())ss.erase(tmp);
    if (ss.size() == 0)
    {
        std::cout << "找不到文件，请确认：\"" << path << "\"路径下至少有一张需要抓取人脸的图片" << std::endl;
        exit(-2);
    }
    FaceExtract fe("model/haarcascade_frontalface_alt2.xml");
    if (fe.HasError())
    {
        std::cout << "人脸捕获器加载失败！" << std::endl;
        exit(-2);
    }
    std::string out_path = OUTPUT_PATH;
    if (out_path[out_path.length() - 1] != '/' || out_path[out_path.length() - 1] != '\\')out_path += '/';
    int id = 1000;
    for (auto & str : ss)
    {
        std::string path = str;
        cv::Mat mat = cv::imread(str);
        if (mat.empty())continue;
        std::cout << "处理：" << str << std::endl;
        cv::UMat frame = mat.getUMat(cv::ACCESS_RW);
        fe.AnalyzeFrame(frame);
        while (fe)
        {
            auto face = fe.GiveFace();
            std::stringstream name;
            name << id;
            std::cout << "输出到：" << out_path + name.str() + ".jpg" << std::endl;
            cv::imwrite(out_path + name.str() + ".jpg", face);
            id++;
        }
    }
    std::cout << "完成！" << std::endl;
}

void Demo()
{
    std::cout << "默认文件寻找路径：" << INPUT_PATH << "/facesfeatures.facedata" << "\n是否需要更改路径(y/n)：";
    char bol;
    std::cin >> bol;
    std::string path = std::string(INPUT_PATH) + "/facesfeatures.facedata";
    if (bol == 'Y' || bol == 'y')
    {
        std::cout << "请输入目标文件：\n";
        std::cin >> path;
    }
    FaceFile ff;
    if (!ff.Load(path))
    {
        std::cout << "读取文件出现错误！\n";
        exit(-2);
    }
    auto rec = ff.MoveList();
    FaceExtract fe("model/haarcascade_frontalface_alt2.xml");
    if (fe.HasError())
    {
        std::cout << "脸部抓取器出现错误！" << std::endl;
        exit(-2);
    }
    cv::VideoCapture video;
    if (!video.open(0))
    {
        std::cout << "open fail!" << std::endl;
        exit(0);
    }
    video.set(CV_CAP_PROP_FPS, 30);
    std::string modelConfiguration = "model/deploy.prototxt";
    std::string modelBinary = "model/vgg_face.caffemodel";
    FaceRec face(modelConfiguration, modelBinary);
    if (!face)
    {
        std::cout << "模型加载错误！" << std::endl;
        exit(-2);
    }
    face.UseGPU(false);
    std::cout << "开始!!!!!!!" << std::endl;
    cv::UMat frame;
    //int i = 0;
    while (1)
    {
        video >> frame;
        cv::resize(frame, frame, cv::Size(),0.5,0.5);
        cv::imshow("cam", frame);
        fe.AnalyzeFrame(frame);
        while (fe)
        {
            auto _face = fe.GiveFace();
            //cv::imshow("face", _face);
            auto fea = face.GetFeatures(_face);
            for (auto f : rec)
            {
                float x = face.Similar(f.features, fea);
                if (x > 0.8)
                {
                    std::cout << "发现:" << f.id << std::endl;
                }
            }
            
        }
        if (cv::waitKey(5) >= 0)break;
    }
    cv::destroyAllWindows();
}



#else

int main()
{
    return 0;
}


#endif
